home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / security / doc / clippings / 920527-01 < prev    next >
Encoding:
Internet Message Format  |  1992-06-04  |  9.7 KB

  1. From: wietse@wzv.win.tue.nl (Wietse Venema)
  2. Newsgroups: comp.unix.programmer,comp.security.misc
  3. Subject: Re: proposal to improve set-uid security
  4. Message-ID: <3348@wzv.win.tue.nl>
  5. Date: 27 May 92 20:29:02 GMT
  6. Organization: Eindhoven University of Technology, The Netherlands
  7.  
  8. This article was inspired by a recently-discovered security problem
  9. with environment variables that control the use of SunOS shared
  10. libraries (see comp.security.announce for the relevant CERT advisory).
  11.  
  12. The problem is not specific to the SunOS environment, though. There is
  13. a basic flaw in the way that most set-xid commands try to circumvent
  14. trojan horses. This article discusses a possible solution.
  15.  
  16. Upon creation, a UNIX process inherits the full environment from its
  17. predecessor.  In the case of set-uid (set-gid) commands, it is up to
  18. the process to sanitize its environment before running other commands.
  19. Cleanup is usually limited to a few environment variables such as PATH
  20. or perhaps IFS, and the remainder of the environment is just passed
  21. on.  The problem is: environment information is considered innocent
  22. until proven guilty.
  23.  
  24. The introduction of user-selectable shared libraries is just an example
  25. of side effects caused by unanticipated environment information. These
  26. and similar unexpected side effects complicate the design of reliable
  27. set-xid commands, because the author of a program cannot possibly know
  28. about all the documented (and undocumented!) environment variables that
  29. may be exploited with present and future UNIX implementations.
  30.  
  31. Censoring of specific environment variables is not a general solution,
  32. especially when done in user-level code: the "black list" would never
  33. be complete.  Selective inheritance of the environment may be a better
  34. alternative.  
  35.  
  36. My proposal is a single user-level function called newenv(). Its
  37. purpose is to create a new environment that is populated only by
  38. explicitly-named environment variables.  The interface is:
  39.  
  40.     int newenv(char *arg1, ..., char *argn, (char *) 0)
  41.  
  42.     If an argument is of the form "name", the corresponding entry from
  43.     the old environment is linked to the new environment.
  44.  
  45.     If an argument is of the form "name=value", the string itself is
  46.     linked to the new environment.
  47.  
  48. Thus, instead of censoring specific environment variables that at some
  49. point in time were proven "guilty" of abuse, a process retains only an
  50. explicitly-enumerated subset of its original environment. The latter
  51. approach should be much more effective against trojan horse attacks
  52. than just fixing IFS, PATH and a few others.
  53.  
  54. Example: many set-uid commands would work fine with something like:
  55.  
  56.     if (newenv("PATH=/bin:/usr/bin:/usr/ucb", (char *) 0)) {
  57.     perror("newenv"); 
  58.     exit(1); 
  59.     }
  60.  
  61. No original environment information would be retained at all, which
  62. eliminates the risk of trojan horses in the process environment.
  63.  
  64. Some set-xid commands commands require that HOME, NAME or ORGANIZATION
  65. be preserved; that could be achieved with:
  66.  
  67.     newenv("PATH=/bin:/usr/bin:/usr/ucb", "HOME", 
  68.         "NAME", "ORGANIZATION", (char *) 0)
  69.  
  70. The proposal allows for a simple and easy to verify implementation.
  71. Attached are a sample implementation and a manual page that gives a
  72. more accurate description than the informal discussion above.
  73.  
  74.     Wietse
  75.  
  76. #! /bin/sh
  77. # This is a shell archive.  Remove anything before this line, then unpack
  78. # it by saving it into a file and typing "sh file".  To overwrite existing
  79. # files, type "sh file -c".  You can also feed this as standard input via
  80. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  81. # will see the following message at the end:
  82. #        "End of shell archive."
  83. # Contents:  newenv.c newenv.3 Makefile
  84. # Wrapped by wietse@wzv on Wed May 27 22:27:47 1992
  85. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  86. if test -f newenv.c -a "${1}" != "-c" ; then 
  87.   echo shar: Will not over-write existing file \"newenv.c\"
  88. else
  89. echo shar: Extracting \"newenv.c\" \(2277 characters\)
  90. sed "s/^X//" >newenv.c <<'END_OF_newenv.c'
  91. X /*
  92. X  * newenv - sanitize process environment
  93. X  * 
  94. X  * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
  95. X  * Computing Science, Eindhoven University of Technology, The Netherlands.
  96. X  */
  97. X
  98. X/* C library */
  99. X
  100. X#ifdef __STDC__
  101. X#include <string.h>
  102. X#include <stdlib.h>
  103. X#else
  104. Xextern char *strchr();
  105. X#endif
  106. X
  107. X/* Attempt to unify the stdarg and varargs interfaces */
  108. X
  109. X#ifdef __STDC__
  110. X#include <stdarg.h>
  111. X#define VARARGS(func,type,arg) func(type arg, ...)
  112. X#define VASTART(ap,type,name)  va_start(ap, name)
  113. X#else
  114. X#include <varargs.h>
  115. X#define VARARGS(func,type,arg) func(va_alist) va_dcl
  116. X#define VASTART(ap,type,name)  type name; va_start(ap); name = va_arg(ap, type)
  117. X#endif
  118. X
  119. X/* findenv - look up environment entry */
  120. X
  121. Xstatic char *findenv(env, name)
  122. Xchar  **env;
  123. Xregister char *name;
  124. X{
  125. X    register char *cp;
  126. X    register int len = strlen(name);
  127. X    register char **cpp;
  128. X
  129. X#define STREQ(x,y,l) (x[0] == y[0] && strncmp(x,y,l) == 0)
  130. X
  131. X    for (cpp = env; cp = *cpp; cpp++)
  132. X    if (STREQ(name, cp, len) && cp[len] == '=')
  133. X        return (cp);
  134. X    return (0);
  135. X}
  136. X
  137. X/* newenv - set up limited environment */
  138. X
  139. Xint     VARARGS(newenv, char *, head)
  140. X{
  141. X    va_list ap;
  142. X    register char *item;
  143. X    register char **old;
  144. X    static char *new[] = {0};
  145. X    extern char **environ;
  146. X
  147. X    VASTART(ap, char *, head);
  148. X
  149. X    /*
  150. X     * Move original environment away and install an empty one. In principle,
  151. X     * this code should work even when we are called more than once. In
  152. X     * practice, most putenv() implementations maintain internal state and
  153. X     * have problems when the environment changes without their consent.
  154. X     */
  155. X
  156. X    old = environ;
  157. X    environ = new;
  158. X
  159. X    /*
  160. X     * Fill in the new environment. If "name=value" is given it is linked to
  161. X     * the new environment; if "name" is given its old environment entry is
  162. X     * linked to the new environment.
  163. X     */
  164. X
  165. X    for (item = head; item; item = va_arg(ap, char *))
  166. X    if (strchr(item, '=') || (item = findenv(old, item)))
  167. X        if (putenv(item))
  168. X        return (-1);
  169. X    return (0);
  170. X}
  171. X
  172. X#ifdef TEST
  173. X
  174. Xmain()
  175. X{
  176. X    int     ret;
  177. X
  178. X    if (ret = newenv("PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin",
  179. X             "HOME",
  180. X             "NAME",
  181. X             "ORGANIZATION",
  182. X             (char *) 0))
  183. X    perror("newenv");
  184. X
  185. X    system("printenv");
  186. X    return (ret);
  187. X}
  188. X
  189. X#endif
  190. END_OF_newenv.c
  191. if test 2277 -ne `wc -c <newenv.c`; then
  192.     echo shar: \"newenv.c\" unpacked with wrong size!
  193. fi
  194. # end of overwriting check
  195. fi
  196. if test -f newenv.3 -a "${1}" != "-c" ; then 
  197.   echo shar: Will not over-write existing file \"newenv.3\"
  198. else
  199. echo shar: Extracting \"newenv.3\" \(2423 characters\)
  200. sed "s/^X//" >newenv.3 <<'END_OF_newenv.3'
  201. X.TH NEWENV 3
  202. X.SH NAME
  203. Xnewenv \- set up restricted environment
  204. X.SH SYNOPSIS
  205. X.na
  206. X.nf
  207. Xint newenv([char *arg1, ..., char *argn, ] (char *) 0)
  208. X.ad
  209. X.fi
  210. X.SH DESCRIPTION
  211. Xnewenv() sets up a new, restricted, environment. Its primary purpose is
  212. Xto protect privileged commands against trojan horse attacks via
  213. Xunexpected environment variables: newenv() retains only an explicitly
  214. Xnamed subset of the process environment.
  215. X.PP
  216. XAfter creating an empty environment, newenv() processes its argument
  217. Xlist.  Arguments can assume the following forms:
  218. X.TP
  219. X"name" 
  220. X.br
  221. XThe corresponding entry from the old environment is linked to
  222. Xthe new environment. A request for a non-existing entry has no effect.
  223. X.TP
  224. X"name=value" 
  225. X.br
  226. XThe "name=value" information is linked to the new
  227. Xenvironment.
  228. X.PP
  229. XThe resulting environment can be accessed in the usual manner with the
  230. Xgetenv() and putenv() library functions.
  231. X.SH EXAMPLES
  232. XA typical set-uid (set-gid) command would do:
  233. X.sp
  234. X.in +2
  235. X.nf
  236. X.na
  237. Xif (newenv("PATH=/bin:/usr/bin:/usr/ucb", (char *) 0)) {
  238. X    perror("newenv");
  239. X    exit(1);
  240. X}
  241. X.fi
  242. X.ad
  243. X.sp
  244. X.PP
  245. XIn this example, no information is retained from the original
  246. Xenvironment. It should take care of all trojans that involve
  247. Xenvironment variables.
  248. X.PP
  249. XSome commands require that the NAME, HOME or ORGANIZATION environment
  250. Xvariables be preserved, too:
  251. X.sp
  252. X.in +2
  253. X.nf
  254. X.na
  255. Xnewenv("PATH=/bin:/usr/bin:/usr/ucb",
  256. X    "NAME", "HOME", "ORGANIZATION", (char *) 0)
  257. X.fi
  258. X.ad
  259. X.PP
  260. Xwould take care of that.
  261. X.SH SEE ALSO
  262. Xputenv(3), getenv(3)
  263. X.SH DIAGNOSTICS
  264. Xnewenv() returns a non-zero value in case of memory-allocation
  265. Xproblems.  Under these conditions, the resulting environment will be
  266. Xonly a subset of what was requested.
  267. X.SH WARNINGS
  268. Xnewenv() uses putenv(), so the usual caveats apply: the third argument
  269. Xto main() is not modified; "name=value" arguments should not rely on
  270. Xautomatic storage that may be reused before the process terminates.
  271. X.sp
  272. XMany putenv() implementations keep internal state and have problems
  273. Xwhen the environment changes between putenv() calls. For this reason,
  274. Xnewenv() calls should not be preceded by any putenv() or newenv()
  275. Xcalls.
  276. X.sp
  277. XSome programs may misbehave when commonly-used environment variables
  278. Xsuch as TERM, HOME and SHELL are missing from the environment.
  279. X.SH AUTHOR(S)
  280. X.na
  281. X.nf
  282. XWietse Venema
  283. XEindhoven University of Technology
  284. XDepartment of Mathematics and Computer Science
  285. XDen Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  286. END_OF_newenv.3
  287. if test 2423 -ne `wc -c <newenv.3`; then
  288.     echo shar: \"newenv.3\" unpacked with wrong size!
  289. fi
  290. # end of overwriting check
  291. fi
  292. if test -f Makefile -a "${1}" != "-c" ; then 
  293.   echo shar: Will not over-write existing file \"Makefile\"
  294. else
  295. echo shar: Extracting \"Makefile\" \(129 characters\)
  296. sed "s/^X//" >Makefile <<'END_OF_Makefile'
  297. XCFLAGS    = -g
  298. X
  299. Xdemo:    newenv
  300. X    ./newenv
  301. X
  302. Xnewenv:    newenv.c
  303. X    $(CC) $(CFLAGS) -DTEST -o $@ newenv.c
  304. X
  305. Xclean:
  306. X    rm -f newenv newenv.o core
  307. END_OF_Makefile
  308. if test 129 -ne `wc -c <Makefile`; then
  309.     echo shar: \"Makefile\" unpacked with wrong size!
  310. fi
  311. # end of overwriting check
  312. fi
  313. echo shar: End of shell archive.
  314. exit 0
  315.  
  316.